package com.youxin.chat.user.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.youxin.auth.server.utils.JwtTokenServerUtils;
import com.youxin.auth.utils.JwtUserInfo;
import com.youxin.auth.utils.Token;
import com.youxin.base.BaseResultCode;
import com.youxin.base.MsgDto;
import com.youxin.chat.basic.dto.constant.*;
import com.youxin.chat.basic.dto.file.FileDto;
import com.youxin.chat.user.context.UserContext;
import com.youxin.chat.user.dto.UserAddDto;
import com.youxin.chat.user.dto.UserInfoDto;
import com.youxin.chat.user.enums.UserCollectEnum;
import com.youxin.chat.user.enums.UserRedisKey;
import com.youxin.chat.user.manager.ImManager;
import com.youxin.chat.user.manager.UserManager;
import com.youxin.chat.user.mapper.*;
import com.youxin.chat.user.model.*;
import com.youxin.chat.user.rpc.FileService;
import com.youxin.chat.user.rpc.SmsService;
import com.youxin.chat.user.service.UserService;
import com.youxin.chat.user.vo.req.*;
import com.youxin.chat.user.vo.user.*;
import com.youxin.common.config.YouxinCommonProperties;
import com.youxin.common.constant.MqConstant;
import com.youxin.common.constant.MsgTypeEnum;
import com.youxin.common.constant.RedisKey;
import com.youxin.dozer.DozerUtils;
import com.youxin.exception.SystemException;
import com.youxin.qiniu.QiniuUtil;
import com.youxin.utils.MD5Util;
import com.youxin.utils.RandomUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.support.atomic.RedisAtomicLong;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.InputStream;
import java.net.URL;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.youxin.common.constant.MqConstant.TOPIC_EXCHANGE;
import static com.youxin.common.constant.RedisKey.SMS_CODE_PREFIX;

/**
 * description: UserServiceImpl <br>
 * date: 2020/2/25 11:36 <br>
 * author: llkj <br>
 * version: 1.0 <br>
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo> implements UserService {

    private static Logger logger = LoggerFactory.getLogger(UserServiceImpl.class);

    @Resource
    private FileService fileService;

    @Resource
    private ImManager imManager;

    @Resource
    private AmqpTemplate amqpTemplate;

    @Resource
    private UserDeviceMapper userDeviceMapper;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private JwtTokenServerUtils jwtTokenServerUtils;

    @Resource
    private DozerUtils dozerUtils;

    @Resource
    private UserFriendMapper userFriendMapper;

    @Resource
    private ActiveUserMapper activeUserMapper;

    @Resource
    private UserCollectMapper userCollectMapper;

    @Resource
    private SmsService smsService;

    @Resource
    private UserManager userManager;

    @Resource
    private YouxinCommonProperties youxinCommonProperties;

    @Override
    public void setLoginPass(String userNo, LoginPassReq loginPass) throws SystemException {
        UserInfo userInfo = this.baseMapper.selectByUserNo(userNo);
        String salt = UserContext.getLoginSalt(userInfo.getMobile());

        if (!StringUtils.isEmpty(userInfo.getPassword())) {
            if (StringUtils.isEmpty(loginPass.getOldPassword())) {
                logger.error("重置密码前请先输入原始密码,userNo={}", userNo);
                throw new SystemException(BaseResultCode.COMMON_FAIL, "请输入原始密码");
            }
            String oldPass = MD5Util.md5Salt(loginPass.getOldPassword().toUpperCase(), salt, 1);
            if (!oldPass.equals(userInfo.getPassword())) {
                logger.error("用户密码不正确，请重新输入,userNo={}", userNo);
                throw new SystemException(BaseResultCode.PASSWORD_INVALID, "用户密码不正确");
            }
        }
        String pass = MD5Util.md5Salt(loginPass.getPassword().toUpperCase(), salt, 1);
        userInfo.setPassword(pass);
        userInfo.setUpdateTime(LocalDateTime.now());
        userManager.updateUserInfo(userInfo);
    }

    @Override
    public void perfectUserInfo(String userNo, UserInfoSetReq req) {
        UserInfo userInfo = this.baseMapper.selectByUserNo(userNo);
        if (userInfo == null) {
            logger.error("获取用户信息失败，data={}", req);
            throw new SystemException(BaseResultCode.RECORD_NOT_EXISTS, "系统内部异常，请联系技术人员");
        }
        if (userInfo.getHasPerfect() == 0) {
            if (StringUtils.isEmpty(req.getNickName()) || StringUtils.isEmpty(req.getAvatar())) {
                logger.error("该用户首次完善用户信息，必填字段不能为空，data={}", req);
                throw new SystemException(BaseResultCode.COMMON_FAIL, "必填字段不能为空");
            }
        }

        if (!StringUtils.isEmpty(req.getNickName())) {
            userInfo.setNickName(req.getNickName());
        }
        if (req.getSex() != null) {
            userInfo.setSex(req.getSex());
        }
        if (!StringUtils.isEmpty(req.getArea())) {
            userInfo.setArea(req.getArea());
        }
        if (!StringUtils.isEmpty(req.getAvatar())) {
            int index = req.getAvatar().lastIndexOf("/");
            if (index > 0) {
                userInfo.setAvatar(req.getAvatar().substring(index + 1));
            } else {
                userInfo.setAvatar(req.getAvatar());
            }
        }
        userInfo.setHasPerfect(1);
        userManager.updateUserInfo(userInfo);
        if (!StringUtils.isEmpty(req.getAvatar()) || !StringUtils.isEmpty(req.getNickName())) {
            amqpTemplate.convertAndSend(TOPIC_EXCHANGE, "user.update", JSONObject.toJSONString(userInfo));
        }

    }

    @Override
    public LoginResp mobileLogin(LoginMobileReq loginMobileReq) throws SystemException {
        UserInfo userInfo = this.baseMapper.selectOne(new LambdaQueryWrapper<UserInfo>().eq(UserInfo::getMobile, loginMobileReq.getMobile()));
        if (userInfo == null) {
            logger.info("手机号不存在，data={}", JSONObject.toJSONString(loginMobileReq));
            throw new SystemException(BaseResultCode.RECORD_NOT_EXISTS, "手机号不存在，请先注册");
        }
        if (userInfo.getUserStatus() == -1) {
            logger.info("该用户被冻结，data={}", JSONObject.toJSONString(loginMobileReq));
            throw new SystemException(BaseResultCode.HAS_NO_PERMISSION, "您的账号存在违反尤信使用规范行为，已被系统冻结");
        }
        String salt = UserContext.getLoginSalt(loginMobileReq.getMobile());
        String password = MD5Util.md5Salt(loginMobileReq.getPassword().toUpperCase(), salt, 1);
        if (!password.equals(userInfo.getPassword())) {
            logger.error("登录失败，密码错误，data={}", JSONObject.toJSONString(loginMobileReq));
            throw new SystemException(BaseResultCode.PASSWORD_INVALID, "密码不正确，请重新输入");
        }
        LoginResp resp = new LoginResp();
        List<UserDevice> userDevices = userDeviceMapper.selectList(new LambdaQueryWrapper<UserDevice>().eq(UserDevice::getUserNo, userInfo.getUserNo()));
        UserDevice userDevice;
        /*
            判断设备是否存在
              若设备不存在，并且不是首次登录，则需要进行短信验证码验证
         */
        if (!CollectionUtils.isEmpty(userDevices)) {
            Optional<UserDevice> optional = userDevices.stream().filter(item -> item.getDeviceId().equals(loginMobileReq.getDeviceId()) && item.getDeviceType().equals(loginMobileReq.getDeviceType())).findAny();
            if (!optional.isPresent()) {
                if (StringUtils.isEmpty(loginMobileReq.getCode())) {
                    logger.info("设备首次登录，data={}", JSONObject.toJSONString(loginMobileReq));
                    throw new SystemException(BaseResultCode.SMS_CODE_NEED, "您的设备第一次登录，需要输入验证码");
                }
                String key = SMS_CODE_PREFIX + loginMobileReq.getMobile() + "." + SmsTypeEnum.LOGIN.getType();
                String code = stringRedisTemplate.opsForValue().get(key);
                if (!code.equalsIgnoreCase(loginMobileReq.getCode())) {
                    logger.info("sms:validateCode error,phone={},type={},code={},needCode={}", loginMobileReq.getMobile(), SmsTypeEnum.LOGIN.getType(), loginMobileReq.getCode(), code);
                    throw new SystemException(BaseResultCode.COMMON_FAIL, "验证码不正确，请重新输入");
                }
                userDevice = new UserDevice();
            } else {
                userDevice = optional.get();
            }
            userDeviceMapper.updateAccessTokenAndImTokenNull(userInfo.getUserNo());
        } else {
            userDevice = new UserDevice();
        }

        JwtUserInfo jwtUserInfo = new JwtUserInfo(userInfo.getUserNo(), loginMobileReq.getDeviceId());
        Token tokenInfo = jwtTokenServerUtils.generateUserToken(jwtUserInfo, 30 * 24 * 60 * 60);
        resp.setToken(tokenInfo.getToken());
        String token = tokenInfo.getToken();
        userDevice.setDeviceType(loginMobileReq.getDeviceType());
        userDevice.setDeviceId(loginMobileReq.getDeviceId());
        userDevice.setLastLoginTime(LocalDateTime.now());
        userDevice.setUpdateTime(LocalDateTime.now());
        userDevice.setUserNo(userInfo.getUserNo());
        userDevice.setAccessToken(tokenInfo.getToken());
        resp.setUserNo(userInfo.getUserNo());
        String imToken = userDevice.getImToken();
        if (StringUtils.isEmpty(imToken)) {
            imToken = imManager.registerUser(userInfo.getUserNo(), userInfo.getNickName(), userInfo.getAvatar());
            resp.setImToken(imToken);
        } else {
            resp.setImToken(imToken);
        }
        userDevice.setCreateTime(LocalDateTime.now());
        userDevice.setImToken(imToken);
        userManager.saveUserDevice(userDevice);
        userInfo.setLastUseTime(LocalDate.now().atStartOfDay());
        userManager.updateUserInfo(userInfo);
        resp.setHasPerfect(userInfo.getHasPerfect());
        return resp;
    }

    @Override
    public void register(RegisterReq registerReq) throws SystemException {

        String token = stringRedisTemplate.opsForValue().get(UserRedisKey.REGISTER_CODE + registerReq.getMobile());
        if (!registerReq.getToken().equals(token)) {
            logger.info("登录用户没有验证短信验证码,data={}", registerReq);
            throw new SystemException(BaseResultCode.RECORD_NOT_EXISTS, "验证码已失效");
        }
        stringRedisTemplate.delete(UserRedisKey.REGISTER_CODE + registerReq.getMobile());
        LambdaQueryWrapper<UserInfo> queryWrapper = new LambdaQueryWrapper<UserInfo>();
        queryWrapper.eq(UserInfo::getMobile, registerReq.getMobile());
        int count = this.baseMapper.selectCount(queryWrapper);
        if (count > 0) {
            throw new SystemException(BaseResultCode.RECORD_EXISTS, "手机号已存在，请登录");
        }
        String salt = UserContext.getLoginSalt(registerReq.getMobile());
        String pass = MD5Util.md5Salt(registerReq.getPassword().toUpperCase(), salt, 1);
        UserInfo userInfo = new UserInfo();
        userInfo.setMobile(registerReq.getMobile());
        String userNo = generateUserNo();
        userInfo.setUserNo(userNo);
        userInfo.setUserStatus(CommonConstant.UserStatus.ON_LINE);
        userInfo.setCreateTime(LocalDateTime.now());
        userInfo.setUpdateTime(LocalDateTime.now());
        userInfo.setNickName(RandomUtil.randomStr(15));
        userInfo.setPassword(pass);
        userInfo.setHasPerfect(0);
        if (!StringUtils.isEmpty(registerReq.getAgentNo())) {
            userInfo.setAgentNo(registerReq.getAgentNo());
        }
        userManager.insertUserInfo(userInfo);
        UserDevice userDevice = new UserDevice();
        userDevice.setDeviceType(registerReq.getDeviceType());
        userDevice.setDeviceId(registerReq.getDeviceId());
        userDevice.setLastLoginTime(LocalDateTime.now());
        userDevice.setUpdateTime(LocalDateTime.now());
        userDevice.setUserNo(userInfo.getUserNo());
        userDeviceMapper.insert(userDevice);
        amqpTemplate.convertAndSend(TOPIC_EXCHANGE, "user.create", userInfo.getUserNo());
    }

    @Override
    public String validateRegisterCode(RegisterCodeReq registerCodeReq) {

        String key = SMS_CODE_PREFIX + registerCodeReq.getMobile() + "." + SmsTypeEnum.REGISTER.getType();
        String code = stringRedisTemplate.opsForValue().get(key);
        if (!registerCodeReq.getCode().equalsIgnoreCase(code)) {
            logger.info("sms:validateCode error,phone={},type={},code={},needCode={}", registerCodeReq.getMobile(), SmsTypeEnum.REGISTER.getType(), registerCodeReq.getCode(), code);
            throw new SystemException(BaseResultCode.COMMON_FAIL, "验证码不正确，请重新输入");
        }

        String token = MD5Util.MD5(UUID.randomUUID().toString());
        String codeKey = UserRedisKey.REGISTER_CODE + registerCodeReq.getMobile();
        stringRedisTemplate.opsForValue().set(codeKey, token, 5, TimeUnit.MINUTES);
        return token;
    }

    @Override
    public void sendSms(RegisterSmsReq smsReqDto, Long ip) throws SystemException {
        String content = "您的短信验证码为%s,该短信验证码在3分钟内有效，此验证码勿提供给他人，请勿转发";
        if (SmsTypeEnum.REGISTER.getType().equals(smsReqDto.getType())) {
            UserSearchVo result = this.baseMapper.selectUserByMobile(smsReqDto.getPhone());
            if (result != null) {
                throw new SystemException(BaseResultCode.RECORD_EXISTS, "该手机号已注册，无需发送短信验证码");
            }
            content = "您的短信验证码为%s,该短信验证码在3分钟内有效，此验证码勿提供给他人，请勿转发";
        } else if (SmsTypeEnum.LOGIN.getType().equals(smsReqDto.getType()) || SmsTypeEnum.FORGET_LOGIN_PASS.getType().equals(smsReqDto.getType())) {
            UserSearchVo result = this.baseMapper.selectUserByMobile(smsReqDto.getPhone());
            if (result == null) {
                throw new SystemException(BaseResultCode.RECORD_NOT_EXISTS, "该手机号尚未注册，请先注册");
            }
            content = "您的短信验证码为%s,该短信验证码在3分钟内有效，此验证码勿提供给他人，请勿转发";
        }
        smsService.sendSms(smsReqDto.getType(), smsReqDto.getPhone(), content, ip);
    }

    @Override
    public void forgetLoginPass(ForgetPassReq forgetPassReq) throws SystemException {
        String key = SMS_CODE_PREFIX + forgetPassReq.getMobile() + "." + SmsTypeEnum.FORGET_LOGIN_PASS.getType();
        String code = stringRedisTemplate.opsForValue().get(key);
        if (!code.equalsIgnoreCase(forgetPassReq.getCode())) {
            logger.info("sms:validateCode error,phone={},type={},code={},needCode={}", forgetPassReq.getMobile(), SmsTypeEnum.FORGET_LOGIN_PASS.getType(), forgetPassReq.getCode(), code);
            throw new SystemException(BaseResultCode.COMMON_FAIL, "验证码不正确，请重新输入");
        }
        UserInfo userInfo = this.baseMapper.selectOne(new LambdaQueryWrapper<UserInfo>().eq(UserInfo::getMobile, forgetPassReq.getMobile()));
        if (userInfo == null) {
            logger.info("用户不存在，mobile={}", forgetPassReq.getMobile());
            throw new SystemException(BaseResultCode.RECORD_NOT_EXISTS, "该手机号对应的用户不存在");
        }
        String salt = UserContext.getLoginSalt(userInfo.getMobile());
        String pass = MD5Util.md5Salt(forgetPassReq.getPassword().toUpperCase(), salt, 1);
        userInfo.setPassword(pass);
        userManager.updateUserInfo(userInfo);
    }

    @Override
    public void logOut(String userNo) {
        userDeviceMapper.updateAccessTokenAndImTokenNull(userNo);

    }

    @Override
    public UserDetailVo selectUserDetails(String userNo) throws SystemException {
        UserInfo userInfo = userManager.selectByUserNo(userNo);
        UserDevice userDevice = userManager.selectActiveByUserNo(userNo);
        if (userInfo == null) {
            logger.error("用户不存在，userNo={}", userNo);
            throw new SystemException(BaseResultCode.RECORD_NOT_EXISTS, "用户不存在");
        }
        UserDetailVo userDetailVo = dozerUtils.map(userInfo, UserDetailVo.class);
        userDetailVo.setAvatar(youxinCommonProperties.getDownloadUrl() + userInfo.getAvatar());
        userDetailVo.setMobile(null);
        userDetailVo.setAgentNo(null);
        if (userDevice != null) {
            userDetailVo.setDeviceType(userDevice.getDeviceType());
            userDetailVo.setDeviceId(userDevice.getDeviceId());
        }
        return userDetailVo;
    }

    @Override
    public UserDetailVo getUserInfo(String userNo) throws SystemException {
        UserInfo userInfo = userManager.selectByUserNo(userNo);
        LocalDateTime lastUserTime = Optional.ofNullable(userInfo.getLastUseTime()).orElse(userInfo.getCreateTime());
        //判断是否是在同一天
        if (lastUserTime.toLocalDate().toEpochDay() - LocalDate.now().toEpochDay() != 0) {
            userManager.saveActiveRecord(userNo);
        }
        UserDetailVo userInfoDto = dozerUtils.map(userInfo, UserDetailVo.class);
        UserDevice userDevice = userDeviceMapper.selectActiveByUserNo(userNo);
        if (userDevice != null) {
            userInfoDto.setDeviceId(userDevice.getDeviceId());
            userInfoDto.setDeviceType(userDevice.getDeviceType());
        }
        userInfoDto.setAvatar(youxinCommonProperties.getDownloadUrl() + userInfo.getAvatar());
        userInfoDto.setAgentNo(null);
        return userInfoDto;
    }

    @Override
    public List<UserDetailVo> listSpecialUsers() throws SystemException {
        List<UserInfo> users = userManager.listSpecialUsers();
        List<UserDetailVo> list = users.stream().map(item -> {
            UserDetailVo vo = dozerUtils.map(item, UserDetailVo.class);
            vo.setAvatar(youxinCommonProperties.getDownloadUrl() + vo.getAvatar());
            return vo;
        }).collect(Collectors.toList());
        return list;
    }

    @Override
    public List<UserMatchVo> matchConcatUser(String userNo, List<UserMatchReq> mobileList) throws SystemException {
        if (CollectionUtils.isEmpty(mobileList)) {
            logger.error("手机号列表为空,userNo={}", userNo);
            throw new SystemException(BaseResultCode.COMMON_FAIL, "无法读取通讯录信息");
        }
        List<String> mobiles = mobileList.stream().map(item -> item.getMobile()).collect(Collectors.toList());
        List<UserInfo> userList = this.baseMapper.selectList(new LambdaQueryWrapper<UserInfo>().in(UserInfo::getMobile, mobiles));
        if (CollectionUtils.isEmpty(userList)) {
            return new ArrayList<>();
        }
        List<String> userNos = userList.stream().map(item -> item.getUserNo()).collect(Collectors.toList());
        List<UserFriend> friendList = userFriendMapper.selectList(new LambdaQueryWrapper<UserFriend>().eq(UserFriend::getUserNo, userNo).in(UserFriend::getFriendNo, userNos));

        Map<String, String> userMap = userList.stream().collect(Collectors.toMap(UserInfo::getMobile, UserInfo::getUserNo));
        Map<String, Integer> friendMap;
        if (CollectionUtils.isEmpty(friendList)) {
            friendMap = new HashMap<>();
        } else {
            friendMap = friendList.stream().collect(Collectors.toMap(UserFriend::getFriendNo, UserFriend::getFriendStatus));
        }
        List<UserMatchVo> list = new ArrayList<>();
        for (UserMatchReq userMatchReq : mobileList) {
            UserMatchVo item = new UserMatchVo();
            list.add(item);
            item.setMobile(userMatchReq.getMobile());
            item.setTrueName(userMatchReq.getTrueName());
            item.setStatus(0);
            if (userMap == null || StringUtils.isEmpty(userMap.get(userMatchReq.getMobile()))) {
                continue;
            }
            String userNoItem = userMap.get(userMatchReq.getMobile());
            item.setUserNo(userNoItem);
            if (friendMap == null || friendMap.get(userNoItem) == null) {
                item.setStatus(1);
            } else {
                item.setStatus(2);
            }
        }
        return list;
    }

    @Override
    public Set selectAssistant() throws SystemException {
        return this.baseMapper.selectAssistant();
    }

    @Override
    public void saveUserName(String userNo, String account) throws SystemException {
        UserInfo userInfo = this.baseMapper.selectByUserNo(userNo);
        if (!StringUtils.isEmpty(userInfo.getUserName())) {
            throw new SystemException(BaseResultCode.RECORD_EXISTS, "已有账号不能修改");
        }

        UserSearchVo userSearchResult = this.baseMapper.selectUserByUserName(account);
        if (userSearchResult == null) {
            userInfo.setUserName(account);
        } else {
            throw new SystemException(BaseResultCode.RECORD_EXISTS, "账号已存在");
        }
        userManager.updateUserInfo(userInfo);
    }

    @Override
    public void shareMsg(ShareMsgReq shareMsgDto, String userId) throws SystemException {
        Map<String, Object> dataMap = new HashMap<>();
        dataMap.put("content", shareMsgDto.getContent());
        dataMap.put("imageUri", shareMsgDto.getImageUri());
        dataMap.put("title", shareMsgDto.getTitle());
        dataMap.put("url", shareMsgDto.getUrl());
        List<Map<Integer, String>> accounts = new ArrayList<>();
        for (ShareMsgUserItem item : shareMsgDto.getAccounts()) {
            Map<Integer, String> map = new HashMap<>();
            map.put(item.getType(), item.getAccount());
            accounts.add(map);
        }
        imManager.sendShareMsg(userId, accounts, dataMap);
    }

    @Override
    public void saveActiveRecord(String userNo) {
        userManager.saveActiveRecord(userNo);
    }

    @Override
    public List<UserCollectVo> collectList(String userNo, CollectSearchReq search) throws SystemException {
        search.setUserNo(userNo);
        List<UserCollectVo> list = userCollectMapper.collectList(new Page<>(search.getCurrentPage(), search.getShowCount()), search);
        list.stream().forEach(item -> {
            if (UserCollectEnum.IMAGE.getType() == item.getContentType() || UserCollectEnum.VIDEO.getType() == item.getContentType()) {
                item.setContentUrl(youxinCommonProperties.getDownloadUrl() + item.getContentUrl());
                item.setContentUrl(youxinCommonProperties.getDownloadUrl() + item.getContent());

            }
        });

        return list;
    }

    @Override
    public void collectAdd(String userNo, UserCollectReq userCollectAdd) throws SystemException {
        Long size = userCollectMapper.getUserCollectSize(userNo);
        if (size > 1024 * 1024L) {
            throw new SystemException(BaseResultCode.COMMON_FAIL, "超出收藏最大容量值(1G)");
        }
        UserCollect userCollect = new UserCollect();
        userCollect.setUserNo(userNo);
        userCollect.setContentFrom(userCollectAdd.getContentFrom());
        userCollect.setContentType(userCollectAdd.getContentType());
        userCollect.setContent(userCollectAdd.getContent());
        userCollect.setCreateTime(LocalDateTime.now());
        userCollect.setFileSize(userCollectAdd.getFileSize());
        if (userCollectAdd.getContentType() == UserCollectEnum.IMAGE.getType()) {
            FileDto fileDto = saveImage(userCollectAdd.getContentUrl(), userNo);
            userCollect.setFileSize(fileDto.getFileSize());
            userCollect.setContent(fileDto.getFileId());
            userCollect.setContentUrl(fileDto.getFileId());
        } else {
            userCollect.setFileSize((long) userCollectAdd.getContent().length());
        }

        userCollectMapper.insert(userCollect);
    }


    private FileDto saveImage(String imageUrl, String userNo) {
        if (StringUtils.isEmpty(imageUrl)) {
            throw new SystemException(BaseResultCode.COMMON_FAIL, "字段信息不完整");
        }
        try {
            URL url = new URL(imageUrl);
            InputStream inputStream = new DataInputStream(url.openStream());
            ByteArrayOutputStream swapStream = new ByteArrayOutputStream();
            byte[] buff = new byte[100];
            int rc = 0;
            while ((rc = inputStream.read(buff, 0, 100)) > 0) {
                swapStream.write(buff, 0, rc);
            }
            byte[] bytes = swapStream.toByteArray();

            FileDto fileDto = new FileDto();
            String filePath = youxinCommonProperties.getFilePath();
            String relFilePath = FileTypeEnum.forType(FileTypeEnum.COLLECT.getType()).getPath();
            String fileId = fileService.primaryId("F");
            filePath += relFilePath + userNo + "/";
            String suffix = "";
            long length = 0;
            try {
                QiniuUtil.upload(bytes, fileId);
            } catch (Exception e) {
                logger.error("创建文件失败", e);
                throw new SystemException(BaseResultCode.INTERNAL_ERROR, "文件上传失败");
            }
            fileDto.setFileId(fileId);
            fileDto.setSuffix(suffix);
            fileDto.setFilePath(relFilePath + userNo + "/");
            fileDto.setFileSize(length);
            fileDto.setFileName(RandomUtil.randomStr(12) + suffix);
            fileDto.setAccount(userNo);
            fileService.add(fileDto, fileDto.getType());
            return fileDto;
        } catch (Exception e) {
            logger.error("转储图片失败", e);
            throw new SystemException(BaseResultCode.INTERNAL_ERROR, "文件上传失败");
        }
    }

    @Override
    public void collectDel(String userNo, int id) throws SystemException {
        UserCollect userCollect = userCollectMapper.selectById(id);
        if (userNo.equalsIgnoreCase(userCollect.getUserNo())) {
            userCollectMapper.deleteById(id);
            if (userCollect.getContentType() == UserCollectEnum.IMAGE.getType() || userCollect.getContentType() == UserCollectEnum.VIDEO.getType()) {
                String content = userCollect.getContent();
                if (StringUtils.isEmpty(content)) {
                    return;
                }
                fileService.removeFile(content);
            }
        } else {
            throw new SystemException(BaseResultCode.HAS_NO_PERMISSION, "无权删除此收藏");
        }
    }

    @Override
    public void setFriendAttr(FriendExtReq friendExtDto, String userNo) throws SystemException {
        UserFriend userFriend = userFriendMapper.selectFriendDetails(userNo, friendExtDto.getFriendNo());
        if (userFriend == null) {
            logger.error("好友不存在,friendNo={},userNo={}", friendExtDto.getFriendNo(), userNo);
            throw new SystemException(BaseResultCode.HAS_NO_PERMISSION, "好友不存在");
        }

        int clearTime = Optional.ofNullable(userFriend.getExt()).map(JSONObject::parseObject).map(item -> item.getInteger("clearTime")).orElse(-1);
        int printScreenNotify = Optional.ofNullable(userFriend.getExt()).map(JSONObject::parseObject).map(item -> item.getInteger("printScreenNotify")).orElse(0);
        JSONObject ext = Optional.ofNullable(userFriend.getExt()).map(JSONObject::parseObject).orElse(new JSONObject());
        Map<String, Object> msgMap = new HashMap<>();
        msgMap.put("friendNo", friendExtDto.getFriendNo());
        msgMap.put("userNo", userNo);
        msgMap.put(friendExtDto.getExtName(), friendExtDto.getExtValue());
        if ("clearTime".equals(friendExtDto.getExtName())) {
            Integer value = Integer.parseInt(friendExtDto.getExtValue());
            if (value > -1 && printScreenNotify != 1) {
                msgMap.put("printScreenNotify", "1");
                ext.put("printScreenNotify", "1");
            }
            ext.put("clearTimePoint", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        } else if ("printScreenNotify".equals(friendExtDto.getExtName())) {
            if (clearTime != -1) {
                throw new SystemException(BaseResultCode.HAS_NO_PERMISSION, "开启了定时清理功能，无法设置截屏通知");
            }
        } else {
            throw new SystemException(BaseResultCode.COMMON_FAIL, "接收参数有误");
        }
        ext.put(friendExtDto.getExtName(), friendExtDto.getExtValue());
        userFriend.setExt(ext.toJSONString());
        userFriendMapper.updateById(userFriend);
        UserFriend friendBy = userFriendMapper.selectFriendDetails(friendExtDto.getFriendNo(), userNo);
        friendBy.setExt(ext.toJSONString());
        userFriendMapper.updateById(friendBy);
        amqpTemplate.convertAndSend(MqConstant.TOPIC_EXCHANGE, "msg.user", new MsgDto(MsgTypeEnum.FRIEND_EXT_CHANGE.getType(), msgMap));

    }

    @Override
    public void updateUserStates(String id, Integer status) {
        UserInfo userInfo = getById(id);
        if (userInfo == null) {
            throw new SystemException(BaseResultCode.RECORD_NOT_EXISTS, "用户不存在");
        }
        if (Integer.compare(CommonConstant.UserStatus.LOCK, status) != 0 && Integer.compare(CommonConstant.UserStatus.ON_LINE, status) != 0) {
            throw new SystemException(BaseResultCode.COMMON_FAIL, "用户参数不正确");
        }
        if (Integer.compare(CommonConstant.UserStatus.LOCK, status) == 0) {
            imManager.addBlockUser(userInfo.getUserNo());
        } else {
            imManager.removeBlockUser(userInfo.getUserNo());
        }
        userInfo.setUserStatus(status);
        userManager.updateUserInfo(userInfo);
    }

    @Override
    public void saveUser(UserAddDto userAddDto) {
        UserInfo userInfo = this.baseMapper.selectByUserNo(userAddDto.getUserNo());
        if (userInfo == null) {
            userInfo = new UserInfo();
        }
        userInfo.setUserNo(userAddDto.getUserNo());
        userInfo.setUserStatus(CommonConstant.UserStatus.ON_LINE);
        userInfo.setNickName(userAddDto.getNickName());
        userInfo.setAvatar(userAddDto.getAvatar());
        userInfo.setUserType(userAddDto.getUserType());
        userInfo.setUserName(userAddDto.getUserName());
        userInfo.setMobile(userAddDto.getMobile());
        userInfo.setPassword(userAddDto.getPassword());
        userInfo.setUserStatus(CommonConstant.UserStatus.ON_LINE);
        userInfo.setCreateTime(LocalDateTime.now());
        userInfo.setUpdateTime(LocalDateTime.now());
        userManager.saveUser(userInfo);
        String token = imManager.registerUser(userInfo.getUserNo(), userInfo.getNickName(), userInfo.getAvatar());
        stringRedisTemplate.opsForValue().set(RedisKey.SPECIAL_USER_IM_TOKEN + userInfo.getUserNo(), token, 30 * 24 * 60 * 60, TimeUnit.SECONDS);
    }

    @Override
    public UserInfoDto getUserInfoByToken(String token) {
        UserInfo userInfo;
        UserDevice userDevice = userManager.selectByAccessToken(token);
        userInfo = userManager.selectByUserNo(userDevice.getUserNo());
        if (userInfo == null) {
            throw new SystemException(BaseResultCode.RECORD_NOT_EXISTS, "用户信息不存在");
        }
        LocalDateTime lastLoginTime = Optional.ofNullable(userInfo.getLastUseTime()).orElse(userInfo.getCreateTime());
        if (LocalDate.now().toEpochDay() - lastLoginTime.toLocalDate().toEpochDay() > 10) {
            logger.error("用户登录已过期，请重新登录，userNo={}", userInfo.getUserNo());
            throw new SystemException(BaseResultCode.RECORD_NOT_EXISTS, "登录过期，请重新登录");

        }
        return dozerUtils.map(userInfo, UserInfoDto.class);
    }


    private String generateUserNo() throws SystemException {

        RedisAtomicLong entityIdCounter = new RedisAtomicLong(RedisKey.USER_NO_GENERATOR, stringRedisTemplate.getConnectionFactory());
        Long increment = entityIdCounter.getAndIncrement();
        String seq = String.format("%06d", increment);
        System.out.println(seq);
        if ((null == increment || increment.longValue() == 0)) {//初始设置过期时间
            long liveTime = Duration.between(LocalDateTime.now(), LocalDate.now().plusDays(1).atStartOfDay()).getSeconds();
            entityIdCounter.expire(liveTime, TimeUnit.SECONDS);
        }
        String userNo = "U" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")) + seq;
        return userNo;
    }

}
